package com.nadostar.extensions.data;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Element;

import com.nadostar.extensions.util.Reloadable;
import com.nadostar.extensions.util.SpringObjectFactory;
import com.nadostar.extensions.util.WatchedObject;

import static com.nadostar.extensions.data.DataHandlerInfo.*;

public class DataHandlerFactory {
	static final String DEFAULT_LOCATION = "data.xml";
	static final Log LOG = LogFactory.getLog(DataHandlerFactory.class);

	/**
	 * DataHandler 참조 객체
	 */
	private static Map<String, DataHandler> handlers = new ConcurrentHashMap<String, DataHandler>();

	private static DataHandler config;

	/**
	 * lucy configuration 파일을 DataHandler 로 변환하여 factory 내에 등록한다.
	 * 
	 * @param element Element
	 * @return DataHandler
	 */
	public static DataHandler setConfig(Element element) {
		return config = wrap(element);
	}

	/**
	 * DataHandlerInfo 의 정보를 이용하여 XML 파일을 읽고, 이를 DataHandler 로 변환하여 factory 내에 등록한다.
	 * 
	 * @param handlerInfo DataHandlerInfo
	 * @return handler DataHandler
	 */
	public static DataHandler init(DataHandlerInfo handlerInfo) {
		DataHandler handler = create(handlerInfo.getType(), handlerInfo.getLocation());
		
		if (handlerInfo.isCached()) {
			handler = new DataHandlerProxy(handler);
		}
		
		((AbstractDataHandler)handler).setHandlerName(handlerInfo.getName());
		
		if (handlerInfo.isReload()) {
			WatchedObject.getInstance().add((AbstractDataHandler)handler);
		}
		
		handlers.put(handlerInfo.getName(), handler);

		return handler;
	}

	/**
	 * Creates the.
	 * 
	 * @param location the location
	 * 
	 * @return the data handler
	 */
	public static DataHandler create(String location) {
		return create(TYPE_JAXP, location);
	}

	/**
	 * 주어진 정보를 기반으로 DataHandler 를 생성한다.
	 * 
	 * @param handlerType String
	 * @param location String
	 * @return handler DataHandler
	 */
	public static DataHandler create(String handlerType, String location) {
		if (StringUtils.isEmpty(location)) {
			location = DEFAULT_LOCATION;
		}
		
		DataHandler handler;
	
		if (TYPE_DOM4J.equalsIgnoreCase(handlerType)) {
			handler = new Dom4jXmlDataHandler(location);
		} else if (TYPE_JAXP.equalsIgnoreCase(handlerType)) {
			handler = new JaxpXmlDataHandler(location);
		} else {
			handler = SpringObjectFactory.getInstance().getObject(handlerType, new Object[] {location});
		}
	
		return handler;
	}

	/**
	 * Wrap.
	 * 
	 * @param root the root
	 * 
	 * @return the data handler
	 */
	public static DataHandler wrap(org.dom4j.Node root) {
		return new Dom4jXmlDataHandler(root);
	}

	/**
	 * Wrap.
	 * 
	 * @param root the root
	 * 
	 * @return the data handler
	 */
	public static DataHandler wrap(org.w3c.dom.Node root) {
		return new JaxpXmlDataHandler(root);
	}

	/**
	 * DataHandler 를 default handler 로 등록한다.
	 * 
	 * @param dataHandler DataHandler
	 * @return DataHandler
	 */
	public static DataHandler init(DataHandler dataHandler) {
		return init(DataHandlerInfo.DEFAULT_NAME, dataHandler);
	}

	/**
	 * DataHandler 를 지정된 이름으로 등록한다.
	 * 
	 * @param name String
	 * @param dataHandler DataHandler
	 * @return DataHandler
	 */
	public static DataHandler init(String name, DataHandler dataHandler) {
		return handlers.put(name, dataHandler);
	}

	/**
	 * default DataHandler 참조 객체를 반환한다.
	 * @return DataHandler
	 * @throws IllegalStateException {@link DataHandler}가 생성되지 않은 상태에서 호출된 경우
	 */
	public static DataHandler getDataHandler() throws IllegalStateException {
		return getDataHandler(DataHandlerInfo.DEFAULT_NAME);
	}

	/**
	 * DataHandler 참조 객체를 반환한다.
	 * 
	 * @param name String
	 * @return dataHandler DataHandler
	 * @throws IllegalStateException {@link DataHandler}가 생성되지 않은 상태에서 호출된 경우
	 */
	public static DataHandler getDataHandler(String name) throws IllegalStateException {
		DataHandler dataHandler = handlers.get(name);
		
		if (dataHandler == null) {
			throw new IllegalStateException("data handler '" + name + "' is not initialized");
		}
		
		return dataHandler;
	}

	/**
	 * lucy configuration 파일에 대한 DataHandler 를 반환한다.
	 *
	 * @return CONFIG DataHandler
	 * @throws IllegalStateException {@link Configuration} 이 초기화 되지 않은 상태에서 호출된 경우
	 */
	public static DataHandler getConfig() throws IllegalStateException {
		if (config == null) {
			throw new IllegalStateException("configuration is not initialized");
		}
		
		return config;
	}

	/**
	 * Destroy all.
	 */
	public static void destroyAll() {
		for (DataHandler handler : handlers.values()) {
			try {
				handler.destroy();
				WatchedObject.getInstance().remove((Reloadable)handler);
			} catch (Exception e) {
				e.getMessage();
			}
		}
		
		handlers.clear();
	}
	
	/**
	 * datahandler name을 key로 하여 datahandler들을 담고 있는 Map을 리턴한다.
	 * @return handlers
	 */
	public static Map<String, DataHandler> getDataHandlers() {
		return handlers;
	}
	
	/**
	 * Destroy.
	 * 
	 * @param name the name
	 */
	public static void destroy(String name) {
		DataHandler dataHandler = handlers.remove(name);
		
		if (dataHandler != null) {
			try {
				dataHandler.destroy();
				WatchedObject.getInstance().remove((AbstractDataHandler)dataHandler);
			} catch (Exception e) {
				e.getMessage();
			}
		}
	}
	
	public static void main(String[] args) {
		String path = new StringBuilder().append("pagination/configs/config[@type='").append("default").append("']").toString();
		DataHandler handler = DataHandlerFactory.create("pagination-data.xml").select(path);
		System.out.println("page-size: " + handler.get("page-size"));
		System.out.println("index-size: " + handler.get("index-size"));
		System.out.println("step-type: " + handler.get("step-type", "index"));
		System.out.println("template-type: " + handler.get("template-type", "default"));
	}
}
